package com.itcoon.transform.base.utils;

import com.itcoon.common.utils.SpringReflectionUtils;
import com.itcoon.transform.base.constants.TransformConstants;
import com.itcoon.transform.base.exception.AssembleException;
import com.itcoon.transform.base.proxy.IProxy;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Optional;

public class TransformServiceUtils {

    public static String formatGetterName(String value) {
        return TransformConstants.GETTER_PREFIX + value.substring(0, 1).toUpperCase() + value.substring(1);
    }

    public static String formatSetterName(String value) {
        return TransformConstants.SETTER_PREFIX + value.substring(0, 1).toUpperCase() + value.substring(1);
    }

    public static String formatDataValue(String dataMethodName) {
        return dataMethodName.substring(3, 4).toLowerCase() + dataMethodName.substring(4);
    }

    public static Object getCascadeObjFromValue(Object pojo, String value) throws Exception {
        if (!value.contains(".")) return pojo;
        Class<?> belongClass = pojo.getClass();
        String[] datas = value.split("\\.");
        Object cascadeObj = pojo;
        for (String data : datas) {
            String cascadeMethodName = TransformConstants.GETTER_PREFIX + data.substring(0, 1).toUpperCase() + data.substring(1);
            Method cascadeMethod = belongClass.getMethod(cascadeMethodName);
            belongClass = cascadeMethod.getReturnType();
            cascadeObj = cascadeMethod.invoke(cascadeObj);
        }
        return cascadeObj;
    }

    public static Object getBelongFromValue(Object pojo, String value){
        Assert.notNull(pojo, "pojo must be not null");
        if (value.equals("") || !value.contains(".")) return pojo;
        Object cascadeObj = pojo;
        String[] datas = value.split("\\.");
        for (int i = 0, n = datas.length - 1; i < n; i++) {
            cascadeObj = cascadeObj == null ? null : SpringReflectionUtils.getMethodValue(cascadeObj, formatGetterName(datas[i]), null, null);
        }
        return cascadeObj;
    }

    public static Method findGetterFromValue(Class<?> originClass, String value) {
        Assert.notNull(originClass, "origin class must be not null");
        Assert.hasText(value, "value must has text.");
        if(!value.contains(".")){
            return ReflectionUtils.findMethod(originClass, formatGetterName(value));
        }
        String[] datas = value.split("\\.");
        Class<?> cascadeClass = originClass;
        for (int i = 0, n = datas.length; i < n; i++) {
            String data = datas[i];
            Method cascadeGetter = ReflectionUtils.findMethod(cascadeClass, TransformConstants.GETTER_PREFIX + data.substring(0,1).toUpperCase() + data.substring(1));
            if(i == n - 1){
                return cascadeGetter;
            }
            if(cascadeGetter == null){
                break;
            }
            cascadeClass = cascadeGetter.getReturnType();
        }
        throw new AssembleException("Illegal originAttribute#" + value);
    }

    /**
     * 是否是代理对象
     * @param clazz
     * @return
     */
    public static boolean isAssembleProxy(Class<?> clazz){
        return clazz.getName().contains("$") && IProxy.class.isAssignableFrom(clazz);
    }

    /**
     * 获取代理对象的原来类型
     * @return
     */
    public static <T,R> Class<?> getProxyOrigin(IProxy<T,R> proxy) {
        Class<? extends Proxy> clazz = (Class<? extends Proxy>) proxy.getClass();
        if(clazz.getName().contains("$$")){
            return clazz.getSuperclass();
        }
        if(Proxy.isProxyClass(clazz)){
            IProxy<T,R> sourceProxy = (IProxy<T,R>)SpringReflectionUtils.getField(proxy, "h");
            return  (Class<?>) SpringReflectionUtils.getField(sourceProxy, "targetClass");
        }
        throw new IllegalArgumentException("UNKNOWN PROXY FOUND");
    }

    /**
     * 获取对象的原型。
     * 1. 代理对象
     * 2. 匿名对象
     *
     */
    public static <T> Class<T> getOriginClass(T source){
        Class<?> sourceType = source.getClass();
        if(isAssembleProxy(sourceType)){
            return getProxyOrigin((IProxy) source);
        }
        if(sourceType.isAnonymousClass()){
            Class<?> superClass = sourceType.getSuperclass();
            if(Object.class.equals(superClass)){
                Type[] genericInterfaces = sourceType.getGenericInterfaces();
                Optional<Type> originClazzOp = Arrays.stream(genericInterfaces).filter(interClazz->((Class)interClazz).isAssignableFrom(sourceType)).findAny();
                return (Class<T>)originClazzOp.orElse(null);
            }
            return (Class<T>)superClass;
        }
        return (Class<T>)sourceType;
    }


}
